package com.tjmc.finance.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tjmc.finance.mapper.TransactionRecordsMapper;
import com.tjmc.finance.service.TransactionRecordsService;
import com.tjmc.finance.util.AesUtil;
import com.tjmc.recruit.admin.api.feign.RemoteParamService;
import com.tjmc.recruit.common.core.exception.CheckedException;
import com.tjmc.recruit.common.core.util.RedisUtils;
import com.tjmc.recruit.finance.api.entity.TransactionRecordsEntity;
import com.tjmc.recruit.finance.api.enums.WxOrderStatusEnum;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationConfig;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiService;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.BeanUtils;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

/**
 * 充值记录表
 *
 * @author recruit
 * @date 2024-11-25 14:17:50
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class TransactionRecordsServiceImpl extends ServiceImpl<TransactionRecordsMapper, TransactionRecordsEntity> implements TransactionRecordsService {

    private final RemoteParamService remoteParamService;
    private Config config;

    @Override
    public TransactionRecordsEntity generationOrder(TransactionRecordsEntity entity) {
        TransactionRecordsEntity orderInfo = new TransactionRecordsEntity();
        //检查必填参数
        checkOrder(entity);
        BeanUtils.copyProperties(entity, orderInfo);
        //生成订单ID
        String snowflakeNextId = IdUtil.getSnowflakeNextIdStr();
        TransactionRecordsEntity info = getById(snowflakeNextId);
        if (info == null) {
            orderInfo.setPayOrderId(snowflakeNextId);
        } else {
            orderInfo.setPayOrderId(IdUtil.getSnowflakeNextIdStr());
        }
        orderInfo.setStatus(WxOrderStatusEnum.GENERATION.getStatus());
        orderInfo.setStatusDescription(WxOrderStatusEnum.GENERATION.getStatusDesc());
        //异步回调地址
        orderInfo.setNotifyUrl("https://admin.nmzpw.net/api/finance/pay/notify");
        // appID, mch ID
        orderInfo.setAppId(remoteParamService.getByKey("WX_APP_ID").getData());
        orderInfo.setMchId(remoteParamService.getByKey("WX_MCH_ID").getData());
        return orderInfo;
    }

    @Override
    public void wxGenOrder(TransactionRecordsEntity payOrderInfo) {
        Map<String, Object> map = new HashMap<>();
        // 构建service
        if (payOrderInfo.getPayWay().equals("NATIVE")) {
            log.info("NATIVE Pay...");
            com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse prepay = getPrepayResponse(payOrderInfo);
            String codeUrl = prepay.getCodeUrl();
            log.info("NATIVE Pay codeUrl:{}", codeUrl);
            if (StringUtils.isEmpty(codeUrl)) {
                throw new CheckedException("NATIVE Pay code url is empty");
            }
            RedisUtils.set(payOrderInfo.getPayOrderId(), codeUrl, 24 * 60 * 60);
        } else {
            log.info("JSAPI Pay...");
            JsapiService service = getJsapiService();
            PrepayRequest request = getPrepayRequest(payOrderInfo);
            // 调用下单方法，得到应答
            PrepayResponse response = service.prepay(request);
            String prepayId = response.getPrepayId();
            log.info("JSAPI Pay prepayId:{}", prepayId);
            if (StringUtils.isEmpty(prepayId)) {
                throw new CheckedException("JSAPI Pay prepayId is empty");
            }
            RedisUtils.set(payOrderInfo.getPayOrderId(), prepayId, 24 * 60 * 60);
        }
    }

    private static @NotNull PrepayRequest getPrepayRequest(TransactionRecordsEntity payOrderInfo) {
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(payOrderInfo.getAmountFen().intValue());
        request.setAmount(amount);
        request.setAppid(payOrderInfo.getAppId());
        request.setMchid(payOrderInfo.getMchId());
        request.setDescription(payOrderInfo.getSubject());
        request.setNotifyUrl(payOrderInfo.getNotifyUrl());
        request.setOutTradeNo(payOrderInfo.getPayOrderId());
        Payer payer = new Payer();
        payer.setOpenid(payOrderInfo.getWxOpenId());
        request.setPayer(payer);
        return request;
    }

    private com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse getPrepayResponse(TransactionRecordsEntity payOrderInfo) {
        NativePayService service = getNativePayService();
        com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest request = new com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest();
        com.wechat.pay.java.service.payments.nativepay.model.Amount amount = new com.wechat.pay.java.service.payments.nativepay.model.Amount();
        amount.setTotal(payOrderInfo.getAmountFen().intValue());
        request.setAmount(amount);
        request.setAppid(payOrderInfo.getAppId());
        request.setMchid(payOrderInfo.getMchId());
        request.setDescription(payOrderInfo.getSubject());
        request.setNotifyUrl(payOrderInfo.getNotifyUrl());
        request.setOutTradeNo(payOrderInfo.getPayOrderId());
        // 调用下单方法，得到应答
        com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse prepay = service.prepay(request);
        return prepay;
    }

    @Override
    public ResponseEntity payNotification(HttpServletRequest request, String notifyData) {
        RequestParam requestParam = getRequestParam(request, notifyData);
        if (config == null) {
            config = getConfig();
        }

        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser((NotificationConfig) config);

        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            log.info("解密后数据transaction：{}", transaction);
            //解析数据
            //更新订单信息
            String outTradeNo = transaction.getOutTradeNo();
            TransactionRecordsEntity orderInfo = baseMapper.selectById(outTradeNo);
            if (orderInfo == null) {
                log.error("order not found");
                return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
            }
            //订单金额比较
            if (orderInfo.getAmountFen().intValue() != transaction.getAmount().getTotal()) {
                log.error("order amount not match");
                return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
            }
            //支付状态
            if (StringUtils.isNotEmpty(transaction.getTradeState().name())) {
                log.info("pay status:{}", transaction.getTradeState().name());
                if (transaction.getTradeState().name().equals(WxOrderStatusEnum.SUCCESS.getStatus())) {
                    log.info("pay success");
                    //支付成功时间
                    orderInfo.setSuccessTime(transaction.getSuccessTime());
                    //发送短信
//                    sendSms(orderInfo);
                }
                orderInfo.setStatus(transaction.getTradeState().name());
            }
            //支付状态描述
            if (StringUtils.isNotEmpty(transaction.getTradeStateDesc())) {
                log.info("pay status desc:{}", transaction.getTradeStateDesc());
                orderInfo.setStatusDescription(transaction.getTradeStateDesc());
            }
            //微信支付订单号
            orderInfo.setTransactionId(transaction.getTransactionId());
            //更新
            baseMapper.updateById(orderInfo);
//            sendMessage(orderInfo, transaction.getTradeState().name());
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return new ResponseEntity<>(HttpStatus.UNAUTHORIZED);
        }
        return new ResponseEntity<>(HttpStatus.OK);
    }

    @Override
    public Map<String, Object> getSignParam(String payOrderId) {
        if (StringUtils.isEmpty(payOrderId)) {
            throw new CheckedException("订单ID不能为空");
        }
        TransactionRecordsEntity payOrderInfo = baseMapper.selectById(payOrderId);
        if (payOrderInfo == null) {
            throw new CheckedException("未查询到订单信息");
        }
        String preOrderId = RedisUtils.get(payOrderId);
        if (StringUtils.isEmpty(preOrderId)) {
            throw new CheckedException("未查询到预下单ID或支付地址");
        }
        Map<String, Object> map = new HashMap<>();
        map.put("payOrderId", payOrderInfo.getPayOrderId());
        if (payOrderInfo.getPayWay().equals("NATIVE")) {
            map.put("codeUrl", preOrderId);
        } else {
            map.put("preOrderId", preOrderId);
            String timestamp = String.valueOf(System.currentTimeMillis());
            String nonceStr = RandomUtil.randomString(32);
            String preOrder = "prepay_id=" + preOrderId;
            map.put("timeStamp", timestamp);
            map.put("nonceStr", nonceStr);
            map.put("package", preOrder);
            map.put("signType", "RSA");
            //生成签名串
            String sign;
            try {
                sign = AesUtil.sign(payOrderInfo.getAppId(), timestamp, nonceStr, preOrder, remoteParamService.getByKey("APICLIENT_KEY").getData());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            log.info("生成微信签名串: {}",sign);
            map.put("paySign", sign);
        }
        map.put("appId", payOrderInfo.getAppId());
        return map;
    }

    /**
     * 构造微信验签方法
     *
     * @param request    请求头
     * @param notifyData 请求数据
     * @return RequestParam
     */
    public static RequestParam getRequestParam(HttpServletRequest request, String notifyData) {
        String signature = request.getHeader("Wechatpay-Signature");
        String timestamp = request.getHeader("Wechatpay-Timestamp");
        String nonce = request.getHeader("Wechatpay-Nonce");
        String serial = request.getHeader("Wechatpay-Serial");
        log.info("请求头参数：signature：{}，timestamp：{}，nonce：{}，serial：{}", signature, timestamp, nonce, serial);
        return new RequestParam.Builder()
                .serialNumber(serial)
                .nonce(nonce)
                .signature(signature)
                .timestamp(timestamp)
                .body(notifyData)
                .build();
    }

    private NativePayService getNativePayService() {
        return new NativePayService.Builder().config(getConfig()).build();
    }

    private JsapiService getJsapiService() {
        return new JsapiService.Builder().config(getConfig()).build();
    }

    /**
     * 构建微信支付配置信息
     *
     * @return
     */
    public Config getConfig() {
        return new RSAAutoCertificateConfig.Builder()
                .merchantId(remoteParamService.getByKey("WX_MCH_ID").getData())
                .privateKeyFromPath(remoteParamService.getByKey("APICLIENT_KEY").getData())
                .merchantSerialNumber(remoteParamService.getByKey("SERIAL_NUMBER").getData())
                .apiV3Key(remoteParamService.getByKey("API_SECRET").getData())
                .build();
    }


    /**
     * 校验订单必填字段
     *
     * @param payOrderInfo 订单信息
     */
    private void checkOrder(TransactionRecordsEntity payOrderInfo) {
        if (payOrderInfo == null) {
            throw new CheckedException("订单信息不能为空");
        }
    }
}
